<?php
/**
 * This class extends AbsStandardAutoloader for loading class according to PSR0-Autoloading.
 * Reference it at http://www.php-fig.org/psr/psr-0/.
 * @author long
 *
 */

namespace Kite\Autoloader\StandardAutoloader;

include 'IStandardAutoloader.php';

class Psr0Autoloader implements IStandardAutoloader
{
	const NS_SEPARATOR = '\\';
	const CLASSNAME_SEPARATOR = '_';
	const FILE_EXTENSION = '.php';
	
	private $namespaces = array();
	
	/**
	 * Input parameter may be similar to:
	 * <code>
	 * $namespaces = array(
	 * 		'Kite'	=> '/path/to/Kite',
	 * );
	 * </code>
	 * @param array $namespaces 
	 */
	public function __construct($namespaces)
	{
		if ($namespaces != null) {
			foreach ($namespaces as $ns => $dir) {
				$ns = $this->normalizeNamespace($ns);
				$dir = $this->normalizeDirectory($dir);
				if ($ns && $dir) {
					$this->namespaces[$ns] = $dir;
				}
				// raise Exception?
			}
		}
		// raise Exception?
	}
	
	/**
	 * Register to SPL autoload stack.
	 */
	public function register()
	{
		spl_autoload_register(array($this, 'autoload'));
	}
	
	/**
	 * Unregister from SPL autoload stack.
	 */
	public function unregister()
	{
		spl_autoload_unregister('autoload');
	}
	
	/**
	 * Register a namespace for autoloading.
	 * @param string $ns namespace
	 * @param string $dir /path/to/namespace
	 * @return bool TRUE if success, FALSE otherwise.
	 */
	public function registerNamespace($ns, $dir)
	{
		if ($ns != null && $dir != null) {
			$this->namespaces[$this->normalizeNamespace($ns)] = $this->normalizeDirectory($dir);
			return true;
		}
		return false;
	}
	
	/**
	 * Unregister a namespace.
	 * @param string $ns namespace
	 * @return bool TRUE if success, FALSE otherwise.
	 */
	public function unregisterNamespace($ns)
	{
		$ns = $this->normalizeNamespace($ns);
		if ($ns != false && isset($this->namespaces[$ns])) {
			unset($this->namespaces[$ns]);
			return true;
		}
		return false;
	}
		
	/**
	 * Normalizes $dir similar to "/path/to/namespace<b>/</b>" 
	 * @param string $dir "/path/to/namespace[<b>/</b>]"
	 * @return string|boolean Directory path has been normalized or FALSE if <b>$dir</b> is null.
	 */
	protected function normalizeDirectory($dir)
	{
		if ($dir != null) {
			$last = substr($dir, -1);
			if ($last == DIRECTORY_SEPARATOR) {
				return $dir;
			} else {
				return $dir.DIRECTORY_SEPARATOR;
			}
		}
		return false;
	}
	
	/**
	 * Normalizes $ns to "<b>\</b>Vendor(\Namespaces)*".
	 * @param string $ns "[<b>\</b>]Vendor(Namespaces\)*".
	 * @return string|boolean Namespace has been normalized or FALSE if $ns is null.
	 */
	protected function normalizeNamespace($ns)
	{
		if ($ns != null) {
			$first = substr($ns, 0, 1);
			if ($first == self::NS_SEPARATOR) {
				return $ns;
			} else {
				return self::NS_SEPARATOR.$ns;
			}
		}
		return false;
	}
	
	/**
	 * Load class form "fully namespace and classname".
	 * Fully namespace and classname has the form "[\]Vendor(\Namespace)*\Classname".
	 * "_" in Namespace has no special meaning.
	 * "_" in Classname will be convert to DIRECTORY_SEPARATOR.
	 * File extension always is <b>".php"</b>.
	 * @param string $fullClassname 
	 */
	protected function autoload($fullClassname)
	{
		// determine position of namespace and classname separator 
		$pos = strrpos($fullClassname, self::NS_SEPARATOR);
		
		$namespace = $this->normalizeNamespace(substr($fullClassname, 0, $pos -1));
		foreach ($this->namespaces as $ns => $dir) {
			if ($ns == $namespace) {
				$cn = substr($fullClassname, $pos + 1);
				$filename = $this->convertClassnameToFilename($cn, $dir);
				if (file_exists($filename)) {
					include $filename;
				}
				// file not existed???
			}
		}
		// no namespace matched???
	}
	
	/**
	 * Convert "_" in classname to DIRCTORY_SEPARATOR,
	 * concatenate $dir and converted Class_Name into "/path/to/Class/Name.php"
	 * @param string $cn Classname
	 * @param string $dir Namespace directory
	 * @return string 
	 */
	private function convertClassnameToFilename($cn, $dir)
	{
		$cn = str_replace(self::CLASSNAME_SEPARATOR,DIRECTORY_SEPARATOR, $cn);
		return $dir.$cn.self::FILE_EXTENSION;
	}
}
?>